<?php
/**
 * Eka Framework 0.3.0
 *
 * @copyright Copyright (c) 2008 Alexey Yu. Chuprina <656b61@gmail.com> (http://php-eka.googlecode.com)
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 *
 * @version $Id$
 */

/**
 * Eka_Application_Controller
 *
 * @package Application
 * @subpackage Controller
 */
abstract class Eka_Application_Controller
{
	/**
	 * @var Eka_Application
	 */
	private $application;

	/**
	 * Controller position in request path
	 *
	 * @var int
	 */
	private $requestPathDeep;

	/**
	 * Action position in request path on controller
	 *
	 * @var int
	 */
	protected $actionPos = 0;

	/**
	 * @var boolean
	 */
	private $stoped = false;

	/**
	 * Current executed action name
	 *
	 * @var string
	 */
	protected $action = null;

	/**
	 * Default View class name
	 *
	 * @var strign
	 */
	protected $defaultView;

	/**
	 * Default View variables
	 *
	 * @var strign
	 */
	protected $defaultViewVars = array();

	/**
	 * Not auto initialize view if it not initialized
	 *
	 * @var bool
	 */
	protected $noView = false;

	/**
	 * @var bool
	 */
	protected $no404 = false;

	/**
	 * @param Eka_Application $application
	 * @param int $deep
	 */
	public function __construct($application, $deep)
	{
		$this->application = $application;
		$this->requestPathDeep = $deep;
		$this->init();
	}

	/**
	 * Stop controller
	 */
	public function stop()
	{
		$this->stoped = true;
	}

	/**
	 * Dispatch controller
	 */
	public function dispatch()
	{
		if ($this->stoped) return;
		$this->action = $this->getRequest()->formatePathSegment($this->getRequestPathSegment($this->actionPos));

		$result = $this->doDispatch();
		if ($this->stoped) return;

		$this->stop();
		$this->prepareResponse($result);
	}

	/**
	 * Do dispatch
	 *
	 * @return mixed
	 */
	protected function doDispatch()
	{
		// pre dispatch
		$result = $this->preDispatch();
		if ($this->stoped) return null;
		if (null !== $result) return $result;

		// action
		if (null === $this->action) {
			$this->action = 'Index';
		}

		$action = 'action' . $this->action;
		if (!method_exists($this, $action)) {
			if (!$this->no404) {
				$result = $this->actionActionNotFound();
				if ($this->stoped) return null;
				return $result;
			}
			$this->action = 'Index';
			$action = 'action' . $this->action;
		}
		$result = $this->$action();
		if ($this->stoped) return null;

		// post dispatch
		$postResult = $this->postDispatch($result);
		if ($this->stoped) return null;
		if (null !== $postResult) $result = $postResult;
		return $result;
	}

	/**
	 * Prepare action response
	 *
	 * @param mixed $content
	 */
	protected function prepareResponse($content = null)
	{
		if (null !== $content) {
			if ($content instanceof Eka_Application_View_Interface) {
				$content = $content->execute();
			}
			else if (!$this->noView) {
				$content = $this->getView()->set($content);
				if (is_array($content)) {
					$content->set($content);
				}
				$content = $content->execute();
			}
			$this->getResponse()->setBody($content);
		}
		else if (!$this->noView) {
			$this->getResponse()->setBody($this->getView()->execute());
		}
	}

	public function redirect($url)
	{
		$this->stop();
		if (!preg_match('~^([a-z]+\://|/)~', $url)){
			$path = get_class($this);
			$path = explode('_', $path);
			array_shift($path);
			if (end($path) === 'Index') {
				array_pop($path);
			}
			$url = '/' . strtolower(join('/', $path)) . '/' . $url;
		}
		$this->application->redirect($url);
	}

	public function route($path)
	{
		$this->stop();
		$this->application->route($path);
	}

	public function action($action)
	{
		$action = ucfirst($action);
		$this->action = $action;
		$action = 'action' . $action;
		return $this->$action();
	}

	public function actionActionNotFound()
	{
		$this->getApplication()->error404();
	}

	/**
	 * Return path relative to controller position
	 *
	 * @return array
	 */
	public function getRequestPathSegments($offset = 0, $length = null)
	{
		return $this->getRequest()->getPathSegments($this->requestPathDeep + $offset, $length);
	}

	/**
	 * Return path segment relative to controller position
	 *
	 * @param integer $pos
	 * @return string
	 */
	public function getRequestPathSegment($pos)
	{
		return $this->getRequest()->getPathSegment($this->requestPathDeep + $pos);
	}

	public function hasRequestPathSegment($pos)
	{
		return null !== $this->getRequestPathSegment($pos);
	}

	/**
	 * Initialize view object
	 *
	 * @param string $file Template file
	 * @param string $layout Layout file
	 * @param string $view View name
	 * @return Eka_Application_View_Interface
	 */
	public function getView($file = null, $layout = null, $view = null)
	{
		$app = $this->getApplication();
		$viewPath = $this->getViewPath();
		$extension = $app->getViewExtension();

		//file
		if (null === $file) {
			$path = $viewPath;

			$file = false;
			if ($this->action === 'Index') {
				if (is_dir($path)) {
					$file = $path . DIRECTORY_SEPARATOR . 'Index.' . $extension;
					if (!is_file($file)) {
						$file = false;
					}
				}
				if (!$file) {
					$file = $path . '.' . $extension;
				}
			} else {
				$file = $path . DIRECTORY_SEPARATOR . $this->action . '.' . $extension;
			}

		} else {
			if (0 === strpos($file, '/')) {
				$file = $app->getViewPath() . $file . $extension;
			} else {
				$file = $viewPath . DIRECTORY_SEPARATOR . $file . '.' . $extension;
			}
		}

		if (!file_exists($file)) {
			include_once 'Eka/Application/Controller/Exception.php';
			$file = str_replace($app->getViewPath(), '', $file);
			$action = $this->getControllerPath('/') . '/' . $this->action;
			throw new Eka_Application_Controller_Exception("Template file for action '$action' not found ('$file')");
		}

		//layout
		if (null === $layout) {
			$path = $viewPath;
			$layout = false;
			if (is_dir($path)) {
				$layout = $path . DIRECTORY_SEPARATOR . $this->action . '_layout.' . $extension;
				if (!is_file($layout)) {
					$layout = $path . DIRECTORY_SEPARATOR . '_layout.' . $extension;
				}
			} else {
				$layout = $path . DIRECTORY_SEPARATOR . '_layout.' . $extension;
			}
			if (!is_file($layout)) {
				$i = substr_count(get_class($this), '_');
				while ($i--) {
					$path = dirname($path);
					$layout = $path . DIRECTORY_SEPARATOR . '_layout.' . $extension;
					if (is_file($layout)) {
						break;
					} else {
						$layout = false;
					}
				}
			}
			if (!$layout) {
				$layout = null;
			}
		} else {
			if (0 === strpos($layout, '/')) {
				$layout = $app->getViewPath() . $layout . '.' . $extension;
			} else {
				$layout = $viewPath . DIRECTORY_SEPARATOR . $layout . '.' . $extension;
			}
		}

		if (null === $view) {
			if (null === $this->defaultView) {
				$view = $app->getDefaultViewClass();
			} else {
				$view = $this->defaultView;
			}
		}

		$view = new $view($this, $file, $layout);

		if (count($this->defaultViewVars)) {
			$view->set($this->defaultViewVars);
		}

		return $view;
	}

	/**
	 * Return path to controller
	 *
	 * @return string
	 */
	protected function getControllerPath($separator = DIRECTORY_SEPARATOR)
	{
		$path = get_class($this);
		$path = explode('_', $path);
		array_shift($path);
		if (end($path) == 'Index') {
			array_pop($path);
		}
		return $separator . (count($path) ? join($separator, $path) : '');
	}

	/**
	 * Return class path
	 *
	 * @return string
	 */
	protected function getViewPath()
	{
		return $this->getApplication()->getViewPath() . $this->getControllerPath();
	}

	/**
	 * Initialize object
	 *
	 * Called from {@link __construct()} as final step of object instantiation.
	 */
	protected function init()
	{}

	/**
	 * Pre-dispatch
	 *
	 * Called before action method
	 */
	protected function preDispatch()
	{}

	/**
	 * Post-dispatch
	 *
	 * Called after action method execution.
	 */
	protected function postDispatch()
	{}

	/**
	 * @return Eka_Application
	 */
	public function getApplication()
	{
		return $this->application;
	}

	/**
	 * @return Eka_Application_Request
	 */
	public function getRequest()
	{
		return $this->application->getRequest();
	}

	/**
	 * @return Eka_Application_Response
	 */
	public function getResponse()
	{
		return $this->application->getResponse();
	}

	/**
	 * @return Eka_Session_Abstract
	 */
	public function getSession()
	{
		return $this->application->getSession();
	}
}